﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace Kokomo.Mirror.CliMetadata.Signatures
{
	class PrimitiveTypeSpec : TypeSpec
	{
		static PrimitiveTypeSpec()
		{
			_primitiveTypeTable = new Dictionary<SignatureElementType, Type>();
			_primitiveTypeTable[SignatureElementType.Boolean] = typeof(bool);
			_primitiveTypeTable[SignatureElementType.Char] = typeof(char);
			_primitiveTypeTable[SignatureElementType.SByte] = typeof(sbyte);
			_primitiveTypeTable[SignatureElementType.Byte] = typeof(byte);
			_primitiveTypeTable[SignatureElementType.Int16] = typeof(short);
			_primitiveTypeTable[SignatureElementType.UInt16] = typeof(ushort);
			_primitiveTypeTable[SignatureElementType.Int32] = typeof(int);
			_primitiveTypeTable[SignatureElementType.UInt32] = typeof(uint);
			_primitiveTypeTable[SignatureElementType.Int64] = typeof(long);
			_primitiveTypeTable[SignatureElementType.UInt64] = typeof(ulong);
			_primitiveTypeTable[SignatureElementType.SingleFloat] = typeof(float);
			_primitiveTypeTable[SignatureElementType.DoubleFloat] = typeof(double);
			_primitiveTypeTable[SignatureElementType.IntPtr] = typeof(IntPtr);
			_primitiveTypeTable[SignatureElementType.UIntPtr] = typeof(UIntPtr);
			_primitiveTypeTable[SignatureElementType.Object] = typeof(object);
			_primitiveTypeTable[SignatureElementType.String] = typeof(string);
			_primitiveTypeTable[SignatureElementType.Void] = typeof(void);

			Boolean = new PrimitiveTypeSpec(SignatureElementType.Boolean);
			Char = new PrimitiveTypeSpec(SignatureElementType.Char);
			SByte = new PrimitiveTypeSpec(SignatureElementType.SByte);
			Byte = new PrimitiveTypeSpec(SignatureElementType.Byte);
			Int16 = new PrimitiveTypeSpec(SignatureElementType.Int16);
			UInt16 = new PrimitiveTypeSpec(SignatureElementType.UInt16);
			Int32 = new PrimitiveTypeSpec(SignatureElementType.Int32);
			UInt32 = new PrimitiveTypeSpec(SignatureElementType.UInt32);
			Int64 = new PrimitiveTypeSpec(SignatureElementType.Int64);
			UInt64 = new PrimitiveTypeSpec(SignatureElementType.UInt64);
			SingleFloat = new PrimitiveTypeSpec(SignatureElementType.SingleFloat);
			DoubleFloat = new PrimitiveTypeSpec(SignatureElementType.DoubleFloat);
			IntPtr = new PrimitiveTypeSpec(SignatureElementType.IntPtr);
			UIntPtr = new PrimitiveTypeSpec(SignatureElementType.UIntPtr);
			Object = new PrimitiveTypeSpec(SignatureElementType.Object);
			String = new PrimitiveTypeSpec(SignatureElementType.String);
			Void = new PrimitiveTypeSpec(SignatureElementType.Void);

			//_typeSpecTable = new CachedLookupTable<SignatureElementType, PrimitiveTypeSpec>(CreateTypeSpec);
		}

		static Dictionary<SignatureElementType, Type> _primitiveTypeTable;
		//static CachedLookupTable<SignatureElementType, PrimitiveTypeSpec> _typeSpecTable;

		public static readonly PrimitiveTypeSpec Boolean;
		public static readonly PrimitiveTypeSpec Char;
		public static readonly PrimitiveTypeSpec SByte;
		public static readonly PrimitiveTypeSpec Byte;
		public static readonly PrimitiveTypeSpec Int16;
		public static readonly PrimitiveTypeSpec UInt16;
		public static readonly PrimitiveTypeSpec Int32;
		public static readonly PrimitiveTypeSpec UInt32;
		public static readonly PrimitiveTypeSpec Int64;
		public static readonly PrimitiveTypeSpec UInt64;
		public static readonly PrimitiveTypeSpec SingleFloat;
		public static readonly PrimitiveTypeSpec DoubleFloat;
		public static readonly PrimitiveTypeSpec IntPtr;
		public static readonly PrimitiveTypeSpec UIntPtr;
		public static readonly PrimitiveTypeSpec Object;
		public static readonly PrimitiveTypeSpec String;
		public static readonly PrimitiveTypeSpec Void;

		public static PrimitiveTypeSpec Get(SignatureElementType elementType)
		{
			//return _typeSpecTable[elementType];
			PrimitiveTypeSpec spec;
			switch (elementType)
			{
				case SignatureElementType.Boolean:
					spec = Boolean;
					break;
				case SignatureElementType.Char:
					spec = Char;
					break;
				case SignatureElementType.SByte:
					spec = SByte;
					break;
				case SignatureElementType.Byte:
					spec = Byte;
					break;
				case SignatureElementType.Int16:
					spec = Int16;
					break;
				case SignatureElementType.UInt16:
					spec = UInt16;
					break;
				case SignatureElementType.Int32:
					spec = Int32;
					break;
				case SignatureElementType.UInt32:
					spec = UInt32;
					break;
				case SignatureElementType.Int64:
					spec = Int64;
					break;
				case SignatureElementType.UInt64:
					spec = UInt64;
					break;
				case SignatureElementType.SingleFloat:
					spec = SingleFloat;
					break;
				case SignatureElementType.DoubleFloat:
					spec = DoubleFloat;
					break;
				case SignatureElementType.IntPtr:
					spec = IntPtr;
					break;
				case SignatureElementType.UIntPtr:
					spec = UIntPtr;
					break;
				case SignatureElementType.Object:
					spec = Object;
					break;
				case SignatureElementType.String:
					spec = String;
					break;
				case SignatureElementType.Void:
					spec = Void;
					break;
				default:
					throw new ArgumentException("The elementType is not a primitive type.");
			}
			return spec;
		}
		private static PrimitiveTypeSpec CreateTypeSpec(SignatureElementType elementType)
		{
			return new PrimitiveTypeSpec(elementType);
		}

		private PrimitiveTypeSpec(SignatureElementType elementType)
		{
			this.elementType = elementType;
		}

		public readonly SignatureElementType elementType;

		public override bool IsComplete
		{
			get { return true; }
		}

		public override Type ResolveType(
			Module module,
			bool throwOnError,
			Type[] genericTypeArgs,
			Type[] genericMethodArgs
			)
		{
			return _primitiveTypeTable[this.elementType];
		}

		public override string ToString()
		{
			return this.elementType.ToString();
		}

		public override bool Matches(
			Type type,
			Module module,
			Type[] genericTypeArguments,
			Type[] genericMethodArguments
			)
		{
			if ((object)type == null) throw new ArgumentNullException("type");

			Type thisType = this.ResolveType(module, false, null, null);
			bool matches =
				MemberEqualityComparer.Equal(type, thisType)
				;
			return matches;
		}
		public override bool Matches(
			Module ownerModule,
			TypeSpec other,
			Module otherModule
			)
		{
			if ((object)ownerModule == null) throw new ArgumentNullException("ownerModule");
			if (other == null) throw new ArgumentNullException("other");
			if ((object)otherModule == null) throw new ArgumentNullException("otherModule");

			PrimitiveTypeSpec otherPrimitive = other as PrimitiveTypeSpec;
			if (otherPrimitive == null) return false;

			bool matches =
				(this.elementType == otherPrimitive.elementType)
				;

			return matches;
		}
	}
}
